名域 ts {
    导出 接口 输出发出_ {
        输出的文件组: 输出的文件_[];
        跳过的输出: 真假;
    }

    导出 接口 输出的文件_ {
        名称: 文字;
        写入字节序标记: 真假;
        文本: 文字;
    }
}

/*@internal*/
名域 ts {
    导出 函数 获取文件输出发出(程序: 程序_, 源文件: 源文件_, 输出仅Dk文件: 真假,
        取消令牌?: 取消令牌_, 自定义转换器?: 自定义转换器_): 输出发出_ {
        常量 输出的文件组: 输出的文件_[] = [];
        常量 输出结果 = 程序.输出(源文件, 写文件, 取消令牌, 输出仅Dk文件, 自定义转换器);
        返回 { 输出的文件组: 输出的文件组, 跳过的输出: 输出结果.输出跳过 };

        函数 写文件(文件名: 文字, 文本: 文字, 写入字节序标记: 真假) {
            输出的文件组.压入({ 名称: 文件名, 写入字节序标记, 文本 });
        }
    }

    导出 接口 构建状态_ {
        /**
         * Information of the 文件 eg. its version, 签名 etc
         */
        文件信息组: 词典_<构建状态_.文件信息_>;
        /**
         * Contains the map of ReferencedSet=Referenced files of the 文件 if module emit is 启用
         * Otherwise 未定
         * Thus non 未定 value indicates, module emit
         */
        只读 引用词典: 词典_<构建状态_.引用集合_> | 未定;
        /**
         * 词典_ of files that have already called update 签名.
         * That means hence forth these files are assumed to have
         * no change in their 签名 for this version of the program
         */
        具有缓存更新共享签名: 词典_<为真>;
        /**
         * Cache of all files excluding 默认 library 文件 for the 当前 program
         */
        除默认支持库外的所有文件: 只读数组_<源文件_> | 未定;
        /**
         * Cache of all the 文件 names
         */
        所有文件名称组: 只读数组_<文字> | 未定;
    }
}

/*@internal*/
名域 ts.构建状态_ {
    /**
     * Information about the source 文件: Its version and optional 签名 from 最后的 emit
     */
    导出 接口 文件信息_ {
        只读 版本: 文字;
        签名: 文字 | 未定;
    }
    /**
     * Referenced files with values for the keys as referenced 文件's 路径 to be true
     */
    导出 类型 引用集合_ = 词典_<为真>;
    /**
     * Compute the 井号_ to store the shape of the 文件
     */
    导出 类型 计算哈希_ = (数据: 文字) => 文字;

    /**
     * Returns true if oldState is reusable, that is the emitKind = module/non module has not changed
     */
    导出 函数 可以重用旧状态(新引用词典: 词典_<引用集合_>, 旧状态: 只读_<构建状态_> | 未定) {
        返回 旧状态 && !旧状态.引用词典 === !新引用词典;
    }

    /**
     * Creates the 状态 of 文件 references and 签名 for the new program from oldState if it is safe
     */
    导出 函数 创建(新程序: 程序_, 旧状态?: 只读_<构建状态_>): 构建状态_ {
        常量 文件信息组 = 创建词典<文件信息_>();
        常量 引用词典 =  创建词典<引用集合_>();
        常量 具有缓存更新共享签名 = 创建词典<为真>();
        常量 使用旧状态 = 可以重用旧状态(引用词典!, 旧状态);

        // Create the reference map, and set the 文件 infos
        循环 (常量 源文件 属于 新程序.获取源文件组()) {
            常量 版本 = 源文件.版本;
            常量 旧信息 = 使用旧状态 && 旧状态!.文件信息组.获取(源文件.路径)!;

            如果 (引用词典) { }

            文件信息组.设置(源文件.路径, { 版本: 版本, 签名: !!旧信息! && (旧信息! 转为 任意).签名 });
        }

        返回 {
            文件信息组,
            引用词典,
            具有缓存更新共享签名,
            除默认支持库外的所有文件: 未定,
            所有文件名称组: 未定
        };
    }

    /**
     * Gets the files affected by the 路径 from the program
     */
    导出 函数 获取受影响文件从(状态: 构建状态_, 当前状态的程序: 程序_, 路径: 路径_, 取消令牌: 取消令牌_ | 未定, 计算哈希: 计算哈希_, 缓存到更新签名?: 词典_<文字>): 只读数组_<源文件_> {
        // Since the operation could be cancelled, the signatures are always stored in the cache
        // They will be commited once it is safe to use them
        // eg when calling this api from tsserver, if there is no cancellation of the operation
        // In the other cases the affected files signatures are commited only after the iteration through the 结果 is complete
        常量 签名缓存 = 缓存到更新签名 || 创建词典();
        常量 源文件 = 当前状态的程序.获取源文件从路径(路径);
        如果 (!源文件) {
            返回 空数组;
        }

        如果 (!更新共享签名(状态, 当前状态的程序, 源文件, 签名缓存, 取消令牌, 计算哈希)) {
            返回 [源文件];
        }

        常量 结果 = (状态.引用词典 ? 获取更新共享受影响文件当模块输出时 : 获取更新共享受影响文件无模块输出)(状态, 当前状态的程序, 源文件, 签名缓存, 取消令牌, 计算哈希);
        如果 (!缓存到更新签名) {
            // Commit all the signatures in the 签名 cache
            更新签名从缓存(状态, 签名缓存);
        }
        返回 结果;
    }

    /**
     * Updates the signatures from the cache into 状态's fileinfo signatures
     * This should be called whenever it is safe to commit the 状态 of the builder
     */
    导出 函数 更新签名从缓存(状态: 构建状态_, 签名缓存: 词典_<文字>) {
        签名缓存.循环执行((签名, 路径) => {
            状态.文件信息组.获取(路径)!.签名 = 签名;
            状态.具有缓存更新共享签名.设置(路径, 为真);
        });
    }

    /**
     * Returns if the shape of the 签名 has changed since 最后的 emit
     */
    函数 更新共享签名(状态: 只读_<构建状态_>, 当前状态的程序: 程序_, 源文件: 源文件_, 缓存到更新签名: 词典_<文字>, 取消令牌: 取消令牌_ | 未定, 计算哈希: 计算哈希_) {
        调试_.断言(!!源文件);

        // If we have cached the 结果 for this 文件, that means hence forth we should assume 文件 shape is uptodate
        如果 (状态.具有缓存更新共享签名.具有(源文件.路径) || 缓存到更新签名.具有(源文件.路径)) {
            返回 为假;
        }

        常量 信息 = 状态.文件信息组.获取(源文件.路径)!;
        调试_.断言(!!信息);

        常量 上个签名 = 信息.签名!;
        变量 最新签名: 文字;
        如果 (源文件.是声明文件) {
            最新签名 = 源文件.版本;
        }
        否则 {
            常量 输出的发出 = 获取文件输出发出(当前状态的程序, 源文件, /*emitOnlyDtsFiles*/ 为真, 取消令牌);
            如果 (输出的发出.输出的文件组 && 输出的发出.输出的文件组.长度 > 0) {
                最新签名 = 计算哈希(输出的发出.输出的文件组[0].文本);
            }
            否则 {
                最新签名 = 上个签名;
            }
        }
        缓存到更新签名.设置(源文件.路径, 最新签名);

        返回 !上个签名 || 最新签名 !== 上个签名;
    }

    /**
     * Get all the dependencies of the 源文件
     */
    导出 函数 获取全部诊断信息(状态: 构建状态_, 当前状态的程序: 程序_, 源文件: 源文件_): 只读数组_<文字> {
        常量 编译选项 = 当前状态的程序.获取编译选项();
        // With --out or --outFile all outputs go into single 文件, all files depend on each other
        如果 (编译选项.outFile || 编译选项.out) {
            返回 获取全部文件名称(状态, 当前状态的程序);
        }

        // If this is non module emit, or its a global 文件, it depends on all the source files
        如果 (!状态.引用词典 || (!仅包含环境模块())) {
            返回 获取全部文件名称(状态, 当前状态的程序);
        }

        // Get the references, traversing deep from the referenceMap
        常量 看见的词典 = 创建词典<为真>();
        常量 队列 = [源文件.路径];
        判断 (队列.长度) {
            常量 路径 = 队列.弹出();
            如果 (!看见的词典.具有(路径!)) {
                看见的词典.设置(路径!, 为真);
                常量 引用组 = 状态.引用词典.获取(路径!);
                如果 (引用组) {
                    常量 迭代器 = 引用组.键组();
                    循环 (变量 { value, done } = 迭代器.next(); !done; { value, done } = 迭代器.next()) {
                        队列.压入(value 转为 路径_);
                    }
                }
            }
        }

        返回 数组从(词典定义的迭代器(看见的词典.键组(), 路径 => {
            常量 文件 = 当前状态的程序.获取源文件从路径(路径 转为 路径_);
            返回 文件 ? 文件.文件名 : 路径;
        }));
    }

    /**
     * Gets the names of all files from the program
     */
    函数 获取全部文件名称(状态: 构建状态_, 当前状态的程序: 程序_): 只读数组_<文字> {
        如果 (!状态.所有文件名称组) {
            常量 源文件组 = 当前状态的程序.获取源文件组();
            状态.所有文件名称组 = 源文件组 === 空数组 ? 空数组 : 源文件组.映射(文件 => 文件.文件名);
        }
        返回 状态.所有文件名称组;
    }

    /**
     * Gets the files referenced by the the 文件 路径
     */
    函数 获取引用按路径(状态: 只读_<构建状态_>, 引用文件路径: 路径_) {
        返回 数组从(词典定义的迭代器(状态.引用词典!.条目组(), ([文件路径, 文件中的引用]) =>
            文件中的引用.具有(引用文件路径) ? 文件路径 转为 路径_ : 未定
        ));
    }

    /**
     * For script files that contains only ambient external modules, although they are not actually external module files,
     * they can only be consumed via importing 元素组 from them. Regular script files cannot consume them. Therefore,
     * there are no point to rebuild all script files if these special files have changed. However, if any 语句
     * in the 文件 is not ambient external module, we treat it as a regular script 文件.
     */
    函数 仅包含环境模块() {
        返回 为假;
    }

    /**
     * Gets all files of the program excluding the 默认 library 文件
     */
    函数 获取除默认支持库以外的所有文件(状态: 构建状态_, 当前状态的程序: 程序_, 第一个源文件: 源文件_): 只读数组_<源文件_> {
        // Use cached 结果
        如果 (状态.除默认支持库外的所有文件) {
            返回 状态.除默认支持库外的所有文件;
        }

        变量 结果: 源文件_[];
        添加源文件(第一个源文件);
        循环 (常量 源文件 属于 当前状态的程序.获取源文件组()) {
            如果 (源文件 !== 第一个源文件) {
                添加源文件(源文件);
            }
        }
        状态.除默认支持库外的所有文件 = 结果! || 空数组;
        返回 状态.除默认支持库外的所有文件;

        函数 添加源文件(源文件: 源文件_) {
            (结果 || (结果 = [])).压入(源文件);
        }
    }

    /**
     * When program emits non modular code, gets the files affected by the 源文件 whose shape has changed
     */
    函数 获取更新共享受影响文件无模块输出(状态: 构建状态_, 当前状态的程序: 程序_, 源码文件包含更新共享: 源文件_) {
        常量 编译选项 = 当前状态的程序.获取编译选项();
        // If `--out` or `--outFile` is specified, any new emit will 结果 in re-emitting the entire project,
        // so returning the 文件 itself is good enough.
        如果 (编译选项 && (编译选项.out || 编译选项.outFile)) {
            返回 [源码文件包含更新共享];
        }
        返回 获取除默认支持库以外的所有文件(状态, 当前状态的程序, 源码文件包含更新共享);
    }

    /**
     * When program emits modular code, gets the files affected by the 源文件 whose shape has changed
     */
    函数 获取更新共享受影响文件当模块输出时(状态: 构建状态_, 当前状态的程序: 程序_, 源码文件包含更新共享: 源文件_, 缓存到更新签名: 词典_<文字>, 取消令牌: 取消令牌_ | 未定, 计算哈希: 计算哈希_ | 未定) {
        如果 (!仅包含环境模块()) {
            返回 获取除默认支持库以外的所有文件(状态, 当前状态的程序, 源码文件包含更新共享);
        }

        常量 编译选项 = 当前状态的程序.获取编译选项();
        如果 (编译选项 && (编译选项.isolatedModules || 编译选项.out || 编译选项.outFile)) {
            返回 [源码文件包含更新共享];
        }

        // Now we need to if each 文件 in the referencedBy 列表 has a shape change as well.
        // Because if so, its own referencedBy files need to be saved as well to make the
        // emitting 结果 consistent with files on disk.
        常量 已查看的文件词典 = 创建词典<源文件_>();

        // Start with the paths this 文件 was referenced by
        已查看的文件词典.设置(源码文件包含更新共享.路径, 源码文件包含更新共享);
        常量 队列 = 获取引用按路径(状态, 源码文件包含更新共享.路径);
        判断 (队列.长度 > 0) {
            常量 当前路径 = 队列.弹出()!;
            如果 (!已查看的文件词典.具有(当前路径)) {
                常量 当前源文件 = 当前状态的程序.获取源文件从路径(当前路径)!;
                已查看的文件词典.设置(当前路径, 当前源文件);
                如果 (当前源文件 && 更新共享签名(状态, 当前状态的程序, 当前源文件, 缓存到更新签名, 取消令牌, 计算哈希!)) {
                    队列.压入(...获取引用按路径(状态, 当前路径));
                }
            }
        }

        // Return 数组 of values that needs emit
        // Return 数组 of values that needs emit
        返回 数组从(词典定义的迭代器(已查看的文件词典.值组(), 值 => 值));
    }
}
